package top.as.sean.biz.system.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import top.as.sean.biz.system.IMenu;
import top.as.sean.common.constant.Constant;
import top.as.sean.common.exception.GlobalException;
import top.as.sean.common.guava.GuavaCache;
import top.as.sean.common.id.SnowflakeId;
import top.as.sean.common.response.RespStatus;
import top.as.sean.common.tree.TreeUtil;
import top.as.sean.dao.dto.system.MenuAdd;
import top.as.sean.dao.dto.system.MenuEdit;
import top.as.sean.dao.dto.system.MenuSearch;
import top.as.sean.dao.entity.pojo.system.Menu;
import top.as.sean.dao.mapper.system.MenuMapper;
import top.as.sean.dao.vo.system.MenuTree;
import top.as.sean.dao.vo.system.RoleMenuTree;

import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 菜单
 *
 * @author: ArchieSean
 * @create: 2022/11/8
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MenuImpl extends ServiceImpl<MenuMapper, Menu> implements IMenu {
    /**
     * 本地缓存
     */
    private final GuavaCache guavaCache;

    /**
     * 新增菜单
     *
     * @param dto 入参
     * @return boolean
     */
    @Override
    public Boolean addMenu(MenuAdd dto) {
        //校验不是菜单，是按钮、方法 时，权限标识符不能重复
        if (StringUtils.hasLength(dto.getMenuType()) && !Constant.MenuType.MENU.getCode().equals(dto.getMenuType())) {
            validatePrem(dto.getPermCode());
        }
        Menu menu = new Menu();
        BeanUtils.copyProperties(dto, menu);
        menu.setMenuId(SnowflakeId.createId());
        return this.save(menu);
    }

    /**
     * 校验权限标识符是否可用，不可用直接异常
     *
     * @param permCode 权限标识符
     */
    private void validatePrem(@NotNull String permCode) {
        boolean flag = this.lambdaQuery().eq(Menu::getPermCode, permCode).exists();
        if (flag) {
            log.info("该权限标识符已存在----->{}", permCode);
            throw new GlobalException(RespStatus.MENU_PERM_CODE_EXISTED);
        }
    }

    /**
     * menu树形结构
     *
     * @param dto 入参
     * @return list
     */
    @Override
    public List<MenuTree> queryMenuTree(MenuSearch dto) {
        List<Menu> list = this.lambdaQuery().like(StringUtils.hasLength(dto.getMenuName()), Menu::getMenuName, dto.getMenuName()).eq(StringUtils.hasLength(dto.getStatus()), Menu::getStatus, dto.getStatus()).eq(StringUtils.hasLength(dto.getMenuType()), Menu::getMenuType, dto.getMenuType()).list();
        List<MenuTree> trees = Lists.newArrayList();
        if (CollectionUtils.isEmpty(list)) {
            return trees;
        }
        //数据转换
        list.forEach(menu -> {
            MenuTree tree = new MenuTree();
            BeanUtils.copyProperties(menu, tree);
            //id设置（前后名称不一致问题导致）
            tree.setId(menu.getMenuId());
            tree.setStatusDict(menu.getStatus());
            tree.setVisibleDict(menu.getVisible());
            tree.setMenuTypeDict(menu.getMenuType());
            tree.setMethodDict(menu.getMethod());
            trees.add(tree);
        });
        try {
            guavaCache.translateList(trees);
        } catch (Exception e) {
            log.error("翻译菜单树形结构失败，原因：", e);
        }
        return TreeUtil.build(trees, BigDecimal.ZERO.longValue(), Comparator.comparing(MenuTree::getOrderNum));
    }

    /**
     * 编辑
     *
     * @param dto 入参
     * @return boolean
     */
    @Override
    public Boolean editMenu(MenuEdit dto) {
        //查询菜单数据（原始数据）
        Menu menu = this.lambdaQuery().eq(Menu::getMenuId, dto.getMenuId()).one();
        if (ObjectUtils.isEmpty(menu)) {
            throw new GlobalException(RespStatus.MENU_NOT_FOUND);
        }
        //校验，是否更改的是权限标识符
        //校验不是菜单，是按钮、方法 时，权限标识符不能重复
        if (StringUtils.hasLength(dto.getMenuType()) && !Constant.MenuType.MENU.getCode().equals(dto.getMenuType()) && !menu.getPermCode().equals(dto.getPermCode())) {
            validatePrem(dto.getPermCode());
        }
        Menu updateMenu = new Menu();
        BeanUtils.copyProperties(dto, updateMenu);
        return updateById(updateMenu);
    }

    /**
     * 删除菜单
     *
     * @param id id
     * @return boolean
     */
    @Override
    public Boolean deleteMenu(Long id) {
        //0. 查询所有菜单
        List<Menu> list = lambdaQuery().eq(Menu::getMenuId, id).or().eq(Menu::getParentId, id).list();
        if (CollectionUtils.isEmpty(list)) {
            throw new GlobalException(RespStatus.MENU_NOT_FOUND);
        }
        List<Long> ids = list.parallelStream().map(Menu::getMenuId).collect(Collectors.toList());
        //1. 删除 关联表数据
        //todo 删除角色-菜单关联数据
        //2. 删除menu表数据

        //获取所有id
        return removeBatchByIds(ids);
    }

    /**
     * 查询正常状态的菜单
     *
     * @return list
     */
    @Override
    public List<RoleMenuTree> queryAllMenu() {
        List<Menu> list = this.lambdaQuery()
                .eq(Menu::getStatus, Constant.CommonStatus.ENABLE.getCode()).list();
        List<RoleMenuTree> result = Lists.newArrayList();
        if (CollectionUtils.isEmpty(list)) {
            return result;
        }
        list.forEach(menu -> {
            RoleMenuTree menuTree = new RoleMenuTree();
            BeanUtils.copyProperties(menu, menuTree);
            menuTree.setId(menu.getMenuId());
            result.add(menuTree);
        });
        return result;
    }

    /**
     * 查询所有菜单
     *
     * @return list
     */
    @Override
    public List<Menu> queryMenu() {
        return this.lambdaQuery()
                .eq(Menu::getStatus, Constant.CommonStatus.ENABLE.getCode())
                .eq(Menu::getMenuType, Constant.MenuType.MENU.getCode())
                .list();
    }
}
